import 'package:built_collection/built_collection.dart';
import 'package:equatable/equatable.dart';
import 'package:mobile_app.core/core/domain/entities/dialect.dart';

part 'lexical_item_progress/learning_lexical_item_progress/learning_event.dart';

part 'lexical_item_progress/learning_lexical_item_progress/learning_lexical_item_progress.dart';

part 'lexical_item_progress/learning_lexical_item_progress/learning_stage.dart';

part 'lexical_item_progress/lexical_item_progress.dart';

part 'lexical_item_progress/previously_known_lexical_item_progress.dart';

part 'lexical_item_progress/learning_lexical_item_progress/repeatable_learning_stage.dart';

part 'lexical_item_progress/learning_lexical_item_progress/suspended_learning_stage.dart';

part 'lexical_item_use_case.dart';

sealed class LexicalItem {
  String get id;

  String get title;

  String get translation;

  BuiltList<LexicalItemUseCase> get useCases;

  LexicalItemProgress? get progress;

  bool get excluded;

  bool get isContainedInLearningQueue;

  bool get isUnknown;

  bool get isUnspecified;

  bool get isBeingLearned;

  bool get isCompletelyLearned;

  bool get isPreviouslyKnown;

  LexicalItem copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  });
}

sealed class _BaseLexicalItem extends Equatable implements LexicalItem {
  const _BaseLexicalItem();

  @override
  bool get isContainedInLearningQueue {
    final _progress = progress;

    if (_progress is! LearningLexicalItemProgress) {
      return false;
    }

    final learningStage = _progress.learningStages.last;

    if (learningStage is! SuspendedLearningStage) {
      return false;
    }

    if (learningStage.learningEvent == LearningEvent.addedToLearningQueue) {
      return true;
    }

    return false;
  }

  @override
  bool get isUnknown {
    final _progress = progress;

    if (_progress is! LearningLexicalItemProgress) {
      return false;
    }

    final learningStage = _progress.learningStages.last;

    if (learningStage is! SuspendedLearningStage) {
      return false;
    }

    if (learningStage.learningEvent == LearningEvent.markedAsUnknown) {
      return true;
    }

    return false;
  }

  @override
  bool get isUnspecified {
    return progress == null;
  }

  @override
  bool get isPreviouslyKnown {
    return progress is PreviouslyKnownLexicalItemProgress;
  }

  @override
  bool get isCompletelyLearned {
    final _progress = progress;

    if (_progress is! LearningLexicalItemProgress) {
      return false;
    }
    
    final learningStage = _progress.learningStages.last;

    if (learningStage.repetitionNumber == kMaxRepetitionNumber) {
      return true;
    }

    return false;
  }

  @override
  bool get isBeingLearned {
    final _progress = progress;

    if (_progress is! LearningLexicalItemProgress) {
      return false;
    }

    final learningStage = _progress.learningStages.last;

    if (learningStage.repetitionNumber < kMaxRepetitionNumber) {
      return true;
    }

    return false;
  }
}

sealed class InbuiltLexicalItem implements LexicalItem {
  BuiltMap<Dialect, String> get transcriptions;

  String? get imageId;

  @override
  InbuiltLexicalItem copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  });
}

sealed class InbuiltWord implements InbuiltLexicalItem {
  int get rank;

  @override
  InbuiltWord copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  });
}

class InbuiltAdjective extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltAdjective({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltAdjective copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltAdjective(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltAdverb extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltAdverb({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltAdverb copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltAdverb(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltConjunction extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltConjunction({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltConjunction copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltConjunction(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltNoun extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltNoun({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltNoun copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltNoun(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltNumeral extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltNumeral({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltNumeral copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltNumeral(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltPreposition extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltPreposition({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltPreposition copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltPreposition(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltPronoun extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltPronoun({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltPronoun copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltPronoun(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltVerb extends _BaseLexicalItem implements InbuiltWord {
  const InbuiltVerb({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  @override
  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltVerb copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltVerb(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class InbuiltPhrasalVerb extends _BaseLexicalItem implements InbuiltLexicalItem {
  const InbuiltPhrasalVerb({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcriptions,
    required this.useCases,
    required this.imageId,
    required this.rank,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  @override
  final BuiltMap<Dialect, String> transcriptions;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final String? imageId;

  final int rank;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcriptions,
      useCases,
      imageId,
      rank,
      progress,
      excluded,
    ];
  }

  @override
  InbuiltPhrasalVerb copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    BuiltMap<Dialect, String> Function()? transcriptions,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    String? Function()? imageId,
    int Function()? rank,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return InbuiltPhrasalVerb(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcriptions: transcriptions == null ? this.transcriptions : transcriptions(),
      useCases: useCases == null ? this.useCases : useCases(),
      imageId: imageId == null ? this.imageId : imageId(),
      rank: rank == null ? this.rank : rank(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}

class CustomLexicalItem extends _BaseLexicalItem implements LexicalItem {
  const CustomLexicalItem({
    required this.id,
    required this.title,
    required this.translation,
    required this.transcription,
    required this.useCases,
    required this.progress,
    required this.excluded,
  });

  @override
  final String id;

  @override
  final String title;

  @override
  final String translation;

  final String? transcription;

  @override
  final BuiltList<LexicalItemUseCase> useCases;

  @override
  final LexicalItemProgress? progress;

  @override
  final bool excluded;

  @override
  List<Object?> get props {
    return [
      id,
      title,
      translation,
      transcription,
      useCases,
      progress,
      excluded,
    ];
  }

  @override
  CustomLexicalItem copyWith({
    String Function()? id,
    String Function()? title,
    String Function()? translation,
    String? Function()? transcription,
    BuiltList<LexicalItemUseCase> Function()? useCases,
    LexicalItemProgress? Function()? progress,
    bool Function()? excluded,
  }) {
    return CustomLexicalItem(
      id: id == null ? this.id : id(),
      title: title == null ? this.title : title(),
      translation: translation == null ? this.translation : translation(),
      transcription: transcription == null ? this.transcription : transcription(),
      useCases: useCases == null ? this.useCases : useCases(),
      progress: progress == null ? this.progress : progress(),
      excluded: excluded == null ? this.excluded : excluded(),
    );
  }
}
